<template>
  <q-field v-model="values" :hint="hintTips" :label="undefined" :loading='loading' :readonly="innerReadonly" borderless
           class="q-picture" lazy-rules="ondemand" no-error-icon v-bind="$attrs">
    <template v-slot:control>
      <q-uploader :accept="accept" :factory="factoryFn" :filter="filterFile" v-bind="uploadBind" @failed="failed"
                  @finish="finishUpload" @start="startUpload" @uploaded="uploaded">
        <template v-slot:header="scope">
          {{ getFiles(scope) }}
          <template v-if="onlyView">
            <div class="q-file--only-view-cls">
              <slot :click="handleView" :files="scope.files" name="default"></slot>
            </div>
          </template>
          <template v-else>
            <q-btn-group outline>
              <q-btn :disable="!scope.files.length" :label="$t('View')" :loading="innerLoading" icon="preview" outline
                     text-color="primary" @click="handleView">
                <template v-if="scope.files.length">
                  <q-badge :class="`text-${BgToColor('info')}`" color="info" floating>
                    {{ scope.files.length }}
                  </q-badge>
                </template>
              </q-btn>
              <q-separator vertical></q-separator>
              <q-btn :disable="innerReadonly" :label="$t('Upload')" :loading="innerLoading" icon="cloud_upload" outline
                     text-color="primary" @click="scope.pickFiles">
                <q-uploader-add-trigger />
              </q-btn>
            </q-btn-group>
          </template>
        </template>
        <template v-slot:list></template>
      </q-uploader>
    </template>
  </q-field>

  <q-modal v-model="modalVisible" :header="`${$t('FileList')}`" :menuBtn="false" :persistent="false" nonScrollArea
           width="50%">
    <qx-table :data="(uploadScope || {}).files" :option="tableOption" class="no-shadow">
      <template v-slot:__status="{ row }">
        <q-circular-progress :value="(row.__progress || 1) * 100" color="primary" font-size="12px" show-value
                             size="50px">
          {{ row.__progressLabel }}
        </q-circular-progress>
      </template>

      <template v-slot:picture="{ row, index }">
        <template v-if="row.__img">
          <q-img :src="row.__img.src" class="rounded-borders cursor-pointer" loading="lazy" sizes="60px"
                 @click.stop="viewPicture(row, index)"></q-img>
        </template>
      </template>

      <template v-slot:menu="{ row, index }">
        <q-btn :loading="innerLoading" color="primary" dense flat icon="cloud_download" round
               @click="downLoadFile(row)" />
        <q-btn v-if="!innerReadonly" :loading="innerLoading" color="negative" dense flat icon="delete" round
               @click="deleteFile(row, index)" />
      </template>
    </qx-table>
  </q-modal>

  <q-preview v-model="dialogVisible" :imgList="imgList" :index="pictureIndex" keyWords="__img.src"></q-preview>
</template>

<script setup>
import { baseURL } from 'boot/env'
import { i18n } from 'boot/i18n'
import QModal from 'components/Global/QModal.vue'
import QxTable from 'components/Global/QxTable.vue'
import QPreview from 'components/Global/Tools/QPreview.vue'
import { format } from 'quasar'
import { postAPI } from 'src/api'
import { responseHandle } from 'src/axios/utils'
import { createDialog, exportedStreamFile } from 'src/utils/utils'
import { userStores } from 'stores/user-stores'
import { computed, inject, ref, toRef, watch } from 'vue'

const { humanStorageSize } = format

const props = defineProps({
  lazyRules: [Boolean, String],
  modelValue: String,
  clearable: Boolean,
  required: Boolean,
  onlyView: Boolean,
  outlined: Boolean,
  readonly: Boolean,
  loading: Boolean,
  change: Function,
  accept: String,
  sName: String,
  bName: String,
  // 最大允许上传个数
  limitNum: {
    type: Number,
    default: 5
  },
  // 单文件最大限制
  isLtXM: {
    type: Number,
    default: 50
  }
})

const BgToColor = inject('BgToColor')

const emits = defineEmits(['update:modelValue', 'change'])

const uploadScope = ref(null)

const innerLoading = ref(false)
const modalVisible = ref(false)
const dialogVisible = ref(false)

const imgList = computed(() => {
  if (!uploadScope.value) return []
  return uploadScope.value.files.filter(el => el.__img)
})

const innerReadonly = computed(() => {
  const { readonly, onlyView } = props
  return readonly || onlyView
})

const hintTips = computed(() => {
  if (innerReadonly.value) return undefined
  const { limitNum, isLtXM } = props
  if (limitNum && limitNum !== Infinity) return `${i18n.t('FileTip1', [limitNum, isLtXM])}`
  return `${i18n.t('FileTip2', [isLtXM])}`
})

const tableOption = computed(() => {
  return {
    menu: true,
    menuWidth: 100,
    rightBtn: false,
    maxHeight: 'inherit',
    column: [{
      label: `${i18n.t('FileName')}`, field: 'name', rowsNum: 3
    }, {
      label: `${i18n.t('FileSize')}`, field: '__sizeLabel', width: 90
    }, {
      label: `${i18n.t('FileStatus')}`, field: '__status', slot: true, width: 90, align: 'center'
    }, {
      label: `${i18n.t('Thumbnail')}`, field: 'picture', slot: true, width: 90, align: 'center'
    }]
  }
})

const uploadHeader = computed(() => {
  return [{
    name: 'Authorization', value: userStores().token
  }, {
    name: 'lang', value: i18n.locale.value
  }]
})

const url = computed(() => `${baseURL}oss/api/ossUploadRecord/batchUpload`)

const maxLtXM = computed(() => {
  return props.isLtXM * 1024 * 1024
})

const filterFile = files => {
  const list = uploadScope.value.files.map(el => el.__key)

  return files.filter(el => !list.includes(`${el.name}${el.size}`))
}

const uploadBind = computed(() => {
  return {
    flat: true,
    batch: true,
    multiple: true,
    label: undefined,
    'auto-upload': true,
    color: 'transparent',
    'max-files': props.limitNum,
    headers: uploadHeader.value,
    readonly: innerReadonly.value,
    'max-file-size': maxLtXM.value
  }
})

const values = ref('')

const formatFile = file => {
  const tempFile = {
    id: file.id, lastModified: +file.endTime, name: file.originalFileName, size: file.sizeBytes,
    __progressLabel: '100.00%', __status: 'uploaded', resultPath: file.resultPath,
    __sizeLabel: file.sizeBytes < 1024 ? `${file.sizeBytes}.0B` : humanStorageSize(file.sizeBytes)
  }

  tempFile.__key = `${file.originalFileName}${file.sizeBytes}`

  if (file.imageFlag === '1') {
    tempFile.__img = {
      src: file.imageSignedUrl
    }
  }

  if (uploadScope.value) uploadScope.value.files.push(tempFile)
}

const initData = () => {
  if (props.modelValue === values.value) return

  if (!props.modelValue) {
    values.value = ''
    if (uploadScope.value) uploadScope.value.reset()
    return
  }

  innerLoading.value = true

  const params = { pageSize: '-1', imageSignedUrlFlag: true, idList: (props.modelValue || '').split(',') }

  postAPI('/oss/api/ossUploadRecord/list', params, data => {
    values.value = props.modelValue
    for (const file of data.rows) {
      formatFile(file)
    }
    innerLoading.value = false
  }, () => {
    innerLoading.value = false
  })
}

watch(toRef(props, 'modelValue'), () => initData(), { immediate: true })

const factoryFn = () => {
  return {
    url: url.value,
    method: 'POST',
    fieldName: 'files',
    formFields: [{
      name: 'serviceName', value: props.sName || 'factory'
    }, {
      name: 'bussinessModule', value: props.bName || 'factory'
    }, {
      name: 'publicRead', value: false
    }]
  }
}

const getFiles = (scope) => {
  uploadScope.value = scope
}

const startUpload = () => {
  innerLoading.value = true
}

const finishUpload = () => {
  innerLoading.value = false
}

const pictureIndex = ref(0)

const failed = info => {
  if (info.xhr.response) {
    const error = JSON.parse(info.xhr.response)
    responseHandle(error.status, error)
  }
}

const formatValues = () => {
  const list = uploadScope.value.files

  values.value = list.map(el => el.id).join(',')

  const valueList = list.map(el => {
    if (el.xhr && el.xhr.response) return JSON.parse(el.xhr.response)
    return { data: [el] }
  }).map(el => el.data).flat()

  if (props.change) props.change(values.value, valueList)
  else emits('change', values.value, valueList)

  emits('update:modelValue', values.value || '')
}

const uploaded = info => {
  if (info.xhr.response) {
    const res = JSON.parse(info.xhr.response)
    if (res.code !== '0') {
      for (const el of info.files) {
        uploadScope.value.updateFileStatus(el, 'failed', el.size)
        uploadScope.value.removeFile(el)
      }
      responseHandle(res.code, res)
      return
    }
    const NUM = info.files.length
    for (let i = 0; i < NUM; i++) {
      info.files[i].id = res.data[i].id
    }
    formatValues()
  }
}

const handleView = () => {
  modalVisible.value = true
}

const viewPicture = (file, i) => {
  if (!(i > -1)) return
  if (file.__status === 'uploading') return
  pictureIndex.value = i
  dialogVisible.value = true
}

const downLoadFile = file => {
  innerLoading.value = true
  exportedStreamFile({
    dicUrl: '/oss/api/ossUploadRecord/download',
    dicQuery: { path: file.resultPath },
    fileName: file.name,
    dicMethod: 'get',
    done: () => {
      innerLoading.value = false
    }
  })
}

const deleteFile = (file, index) => {
  createDialog({ msg: `${i18n.t('DelTips')}` }, () => {
    uploadScope.value.files.splice(index, 1)
    uploadScope.value.removeFile(file)
    formatValues()
  }, () => {
  })
}

</script>